<?php
/**
 * matser只负责创建worker
 * socket的创建,bind,listen,accept都在自己的worker里，
 */

$worker_num=3;
$title="socket_fork";
define('TITLE_PREFIX','socket_fork_reuseport');
$address = '0.0.0.0';
$port = $argv[1] ?? 8071;

cli_set_process_title( TITLE_PREFIX."-master" );


for($i=0;$i<$worker_num;$i++){
    $pid=pcntl_fork();
    if($pid<0 ){
        echo "fork err".PHP_EOL;
        exit();
    }
    //child
    if (0 == $pid) {
        worker($i);
    }
}


while($pid=pcntl_wait($status)){
    echo $pid." exit".PHP_EOL;
}


function worker($i){
    global $port,$address;
    $listen = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    if (false === $listen) errhandle(__LINE__);

    if (true !== socket_set_option($listen, SOL_SOCKET, SO_REUSEADDR, 1)) errhandle(__LINE__);
    //这项拿掉会出现地址占用的报错
    if (true !== socket_set_option($listen, SOL_SOCKET, SO_REUSEPORT, 1)) errhandle(__LINE__);;

    if (true !== socket_bind($listen, $address, $port)) errhandle(__LINE__);;
    if (true !== socket_listen($listen, 5)) errhandle(__LINE__);;  //待连接队列长度


    echo "Server linsten on:{$address}:$port" . PHP_EOL;
    cli_set_process_title( TITLE_PREFIX."-child"."-".$i );
    while (true) {
        $sock_client = socket_accept($listen);
        if ($sock_client) {
           processClientConn($sock_client);
        }
    }
}

//处理已经连入的连接
function processClientConn($sock_client)
{
    if (socket_getpeername($sock_client, $clinet_addr, $client_port)) {
        echo posix_getpid()." New client " . intval($sock_client) . " come from  {$clinet_addr}:$client_port" . PHP_EOL;
        sayWelcome($sock_client);
    }
    while (true) {
        //接收到不少于len
        $len = socket_recv($sock_client, $buf, 2048, 0);
        if ($len === false) {
            echo "no data" . PHP_EOL;
            continue;
        } elseif ($len === 0) {
            errhandle(__LINE__,false);
            socket_shutdown($sock_client);
            break;
        } else {
            echo posix_getpid()." recv:{" . $buf . "}len=" . $len . PHP_EOL;
            if ($buf == 'quit') {
                socket_shutdown($sock_client);
                break;
            }
        }
    }
}

function errhandle($line_num,$exit=true)
{
    echo $line_num.":".socket_last_error() . ":" . socket_strerror(socket_last_error()) . PHP_EOL;
    if($exit){
        exit();
    }

}
function sayWelcome($client)
{
    $buf = date("H:i:s") . " welcome to server! you id:" . intval($client) . PHP_EOL;
    socket_write($client, $buf, strlen($buf));
}



